| @@ -25,6 +25,13 @@ module Agents | ||
| 25 | 25 |  | 
| 26 | 26 | If `emit_events` is set to `true`, the server response will be emitted as an Event and can be fed to a WebsiteAgent for parsing (using its `data_from_event` and `type` options). No data processing | 
| 27 | 27 | will be attempted by this Agent, so the Event's "body" value will always be raw text. | 
| 28 | + The Event will also have a "headers" hash and a "status" integer value. | |
| 29 | + Set `event_headers_style` to one of the following values to normalize the keys of "headers" for downstream agents' convenience: | |
| 30 | + | |
| 31 | + * `capitalized` (default) - Header names are capitalized; e.g. "Content-Type" | |
| 32 | + * `downcased` - Header names are downcased; e.g. "content-type" | |
| 33 | + * `snakecased` - Header names are snakecased; e.g. "content_type" | |
| 34 | + * `raw` - Backward compatibility option to leave them unmodified from what the underlying HTTP library returns. | |
| 28 | 35 |  | 
| 29 | 36 | Other Options: | 
| 30 | 37 |  | 
| @@ -92,6 +99,12 @@ module Agents | ||
| 92 | 99 | errors.add(:base, "if provided, emit_events must be true or false") | 
| 93 | 100 | end | 
| 94 | 101 |  | 
| 102 | + begin | |
| 103 | +        normalize_response_headers({}) | |
| 104 | + rescue ArgumentError => e | |
| 105 | + errors.add(:base, e.message) | |
| 106 | + end | |
| 107 | + | |
| 95 | 108 | unless %w[post get put delete patch].include?(method) | 
| 96 | 109 | errors.add(:base, "method must be 'post', 'get', 'put', 'delete', or 'patch'") | 
| 97 | 110 | end | 
| @@ -124,6 +137,29 @@ module Agents | ||
| 124 | 137 |  | 
| 125 | 138 | private | 
| 126 | 139 |  | 
| 140 | + def normalize_response_headers(headers) | |
| 141 | + case interpolated['event_headers_style'] | |
| 142 | + when nil, '', 'capitalized' | |
| 143 | +        normalize = ->name { | |
| 144 | +          name.gsub(/(?:\A|(?<=-))([[:alpha:]])|([[:alpha:]]+)/) { | |
| 145 | + $1 ? $1.upcase : $2.downcase | |
| 146 | + } | |
| 147 | + } | |
| 148 | + when 'downcased' | |
| 149 | + normalize = :downcase.to_proc | |
| 150 | + when 'snakecased', nil | |
| 151 | +        normalize = ->name { name.tr('A-Z-', 'a-z_') } | |
| 152 | + when 'raw' | |
| 153 | +        normalize = ->name { name }  # :itself.to_proc in Ruby >= 2.2 | |
| 154 | + else | |
| 155 | + raise ArgumentError, "if provided, event_headers_style must be 'capitalized', 'downcased', 'snakecased' or 'raw'" | |
| 156 | + end | |
| 157 | + | |
| 158 | +      headers.each_with_object({}) { |(key, value), hash| | |
| 159 | + hash[normalize[key]] = value | |
| 160 | + } | |
| 161 | + end | |
| 162 | + | |
| 127 | 163 |      def handle(data, payload = {}) | 
| 128 | 164 | url = interpolated(payload)[:post_url] | 
| 129 | 165 | headers = headers() | 
| @@ -156,7 +192,11 @@ module Agents | ||
| 156 | 192 | } | 
| 157 | 193 |  | 
| 158 | 194 | if boolify(interpolated['emit_events']) | 
| 159 | -        create_event payload: { body: response.body, headers: response.headers, status: response.status } | |
| 195 | +        create_event payload: { | |
| 196 | + body: response.body, | |
| 197 | + headers: normalize_response_headers(response.headers), | |
| 198 | + status: response.status | |
| 199 | + } | |
| 160 | 200 | end | 
| 161 | 201 | end | 
| 162 | 202 | end | 
| @@ -0,0 +1,11 @@ | ||
| 1 | +class PostAgentSetEventHeaderStyle < ActiveRecord::Migration | |
| 2 | + def up | |
| 3 | +    Agent.of_type("Agents::PostAgent").each do |post_agent| | |
| 4 | + if post_agent.send(:boolify, post_agent.options['emit_events']) && | |
| 5 | +         !post_agent.options.key?('event_headers_style') | |
| 6 | + post_agent.options['event_headers_style'] = 'raw' | |
| 7 | + post_agent.save! | |
| 8 | + end | |
| 9 | + end | |
| 10 | + end | |
| 11 | +end | 
| @@ -52,7 +52,7 @@ describe Agents::PostAgent do | ||
| 52 | 52 |            raise "unexpected Content-Type: #{content_type}" | 
| 53 | 53 | end | 
| 54 | 54 | end | 
| 55 | -      { status: 200, body: "<html>a webpage!</html>", headers: { 'Content-Type' => 'text/html' } } | |
| 55 | +      { status: 200, body: "<html>a webpage!</html>", headers: { 'Content-type' => 'text/html' } } | |
| 56 | 56 | } | 
| 57 | 57 | end | 
| 58 | 58 |  | 
| @@ -226,10 +226,28 @@ describe Agents::PostAgent do | ||
| 226 | 226 | expect(@checker.events.last.payload['body']).to eq '<html>a webpage!</html>' | 
| 227 | 227 | end | 
| 228 | 228 |  | 
| 229 | - it "emits the response headers" do | |
| 229 | + it "emits the response headers capitalized by default" do | |
| 230 | 230 | @checker.check | 
| 231 | 231 |            expect(@checker.events.last.payload['headers']).to eq({ 'Content-Type' => 'text/html' }) | 
| 232 | 232 | end | 
| 233 | + | |
| 234 | + it "emits the response headers capitalized" do | |
| 235 | + @checker.options['event_headers_style'] = 'capitalized' | |
| 236 | + @checker.check | |
| 237 | +          expect(@checker.events.last.payload['headers']).to eq({ 'Content-Type' => 'text/html' }) | |
| 238 | + end | |
| 239 | + | |
| 240 | + it "emits the response headers downcased" do | |
| 241 | + @checker.options['event_headers_style'] = 'downcased' | |
| 242 | + @checker.check | |
| 243 | +          expect(@checker.events.last.payload['headers']).to eq({ 'content-type' => 'text/html' }) | |
| 244 | + end | |
| 245 | + | |
| 246 | + it "emits the response headers snakecased" do | |
| 247 | + @checker.options['event_headers_style'] = 'snakecased' | |
| 248 | + @checker.check | |
| 249 | +          expect(@checker.events.last.payload['headers']).to eq({ 'content_type' => 'text/html' }) | |
| 250 | + end | |
| 233 | 251 | end | 
| 234 | 252 | end | 
| 235 | 253 | end |